#region

using System.Collections.Concurrent;
using System.Reflection;

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.DependencyModel;

using Swashbuckle.AspNetCore.SwaggerGen;

#endregion

namespace Leo.Core.Swagger
{
    internal static class Helper
    {
        private static readonly ConcurrentDictionary<Type, IEnumerable<GroupApiInfo>> ServiceGroupsCaches;
        private static readonly ConcurrentDictionary<MethodInfo, IEnumerable<GroupApiInfo>> MethodGroupsCached;
        private static readonly List<GroupApiInfo> DefaultGroups;

        static Helper()
        {
            MethodGroupsCached = new ConcurrentDictionary<MethodInfo, IEnumerable<GroupApiInfo>>();
            ServiceGroupsCaches = new ConcurrentDictionary<Type, IEnumerable<GroupApiInfo>>();
            DefaultGroups = new List<GroupApiInfo> { GetGroupApiInfo(Const.DefaultGroupName) };
        }

        internal static OpenApiInfoEx GetOpenApiInfo(string group)
        {
            return ApiConfig.ApiList.FirstOrDefault(x => x.Group == group) ?? new OpenApiInfoEx { Group = group };
        }

        /// <summary>
        ///     获取所有程序 xml 文件，用于生成接口文档
        /// </summary>
        /// <returns></returns>
        internal static IEnumerable<string> GetXmlDocumentFileNameList()
        {
            var documentFileNameList = new List<string>();
            var libTypeList = new List<string> { "project", "package" };
            var fileNameList = DependencyContext.Default.CompileLibraries
                .Where(lib => libTypeList.Contains(lib.Type)
                              && (lib.Name.StartsWith("Leo.Core.", StringComparison.CurrentCultureIgnoreCase)
                                  || lib.Name.StartsWith("Leo.Bsjc.", StringComparison.CurrentCultureIgnoreCase))
                              && !lib.Name.StartsWith("Microsoft.", StringComparison.CurrentCultureIgnoreCase)
                              && !lib.Name.StartsWith("System.", StringComparison.CurrentCultureIgnoreCase)
                              && !lib.Name.StartsWith("runtime.", StringComparison.CurrentCultureIgnoreCase))
                .Select(lib => $"{lib.Name}.xml").ToList();

            foreach (var fileName in fileNameList)
            {
                string documentFileName = Path.Combine(AppContext.BaseDirectory, fileName);
                if (File.Exists(documentFileName))
                {
                    documentFileNameList.Add(documentFileName);
                }
            }

            var pluginPath = Path.Combine(AppContext.BaseDirectory, "plugins");
            if (Directory.Exists(pluginPath))
            {
                var files = Directory.GetFiles(pluginPath, "*.xml", SearchOption.AllDirectories);
                foreach (var xmlFile in files)
                {
                    documentFileNameList.Add(xmlFile);
                }
            }

            return documentFileNameList;
        }

        internal static bool CheckApiInGroup(string current, ApiDescription desc)
        {
            if (!desc.TryGetMethodInfo(out var method)
                || typeof(Controller).IsAssignableFrom(method.ReflectedType))
            {
                return false;
            }

            var list = GetMethodGroupInfo(method);
            var b = list.Any(u => u.Group.Equals(current, StringComparison.OrdinalIgnoreCase));
            return b;
        }

        internal static bool CheckApiDescription(string name, ApiDescription desc)
        {
            if (!desc.TryGetMethodInfo(out MethodInfo methodInfo))
            {
                return false;
            }

            if (methodInfo.DeclaringType == null)
            {
                return false;
            }

            var groups = methodInfo.DeclaringType
                .GetCustomAttributes(true)
                .OfType<ApiExplorerSettingsAttribute>()
                .ToList();
            //var versions = methodInfo.DeclaringType
            //    .GetCustomAttributes(true)
            //    .OfType<ApiVersionAttribute>()
            //    .SelectMany(attr => attr.Versions);
            if (groups.Count == 0 && name.Equals(Const.DefaultGroupName, StringComparison.Ordinal))
            {
                return true; //内置的或没有分组的
            }

            return groups.Any(x => x.GroupName == name);
        }

        private static IEnumerable<GroupApiInfo> GetServiceGroupInfo(Type type)
        {
            return ServiceGroupsCaches.GetOrAdd(type, GetServiceApiInfo);
        }

        private static IEnumerable<GroupApiInfo> GetServiceApiInfo(Type type)
        {
            if (!type.IsDefined(typeof(ApiDescAttribute), true))
            {
                return DefaultGroups;
            }

            var desc = type.GetCustomAttribute<ApiDescAttribute>(false);
            if (desc == null || desc.Groups.Length == 0)
            {
                if (!string.IsNullOrEmpty(desc.GroupName))
                {
                    return new List<GroupApiInfo> { new() { Group = desc.GroupName } };
                }

                return DefaultGroups;
            }

            return GetGroupApiInfoList(desc.Groups);
        }

        private static IEnumerable<GroupApiInfo> GetMethodGroupInfo(MethodInfo method)
        {
            return MethodGroupsCached.GetOrAdd(method, GetMethodApiInfo);
        }

        private static IEnumerable<GroupApiInfo> GetMethodApiInfo(MethodInfo method)
        {
            if (!method.IsDefined(typeof(ApiDescAttribute), true))
            {
                return GetServiceGroupInfo(method.ReflectedType);
            }

            var desc = method.GetCustomAttribute<ApiDescAttribute>(true);
            if (desc == null || desc.Groups.Length == 0)
            {
                return GetServiceGroupInfo(method.ReflectedType);
            }

            return GetGroupApiInfoList(desc.Groups);
        }

        private static IEnumerable<GroupApiInfo> GetGroupApiInfoList(string[] groups)
        {
            var groupInfo = new List<GroupApiInfo>();
            foreach (var group in groups)
            {
                groupInfo.Add(GetGroupApiInfo(group));
            }

            return groupInfo;
        }

        internal static GroupApiInfo GetGroupApiInfo(string group)
        {
            var order = 0;
            var info = GetOpenApiInfo(group);
            return new GroupApiInfo { Group = group, Order = info.Order ?? order, Visible = info.Visible ?? true };
        }
    }
}